//
// Copyright (C) 2010-2011 Tarmin Rehve <tarmin.rehve@gmail.com>
//
//This file is part of the LibSndFile.NET software library.
//
//    LibSndFile.NET is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    LibSndFile.NET is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Lesser License for more details.
//
//    You should have received a copy of the GNU Lesser Public License
//    along with LibSndFile.NET.  If not, see <http://www.gnu.org/licenses/>.


#pragma once

#include <sndfile.h>
#include <windows.h>
#include <io.h>
#include <Fcntl.h>


using namespace System;
using namespace System::Runtime::Remoting;
using namespace System::Runtime::InteropServices;
using namespace System::IO;


namespace LibSndFile {


    [SerializableAttribute]
    [ComVisibleAttribute(true)]
    ///<summary>Major format types (in SF_FORMAT_...) as defined by the <a href="http://www.mega-nerd.com/libsndfile">libsndfile</a>.</summary>
    ///<remarks>A file type would consist of a major type (ie <see cref="SndFileMajorFormat"/>::WAV) bitwise
    /// ORed with a sub type (ie SndFileSubFormat::PCM_S8).</remarks>
    public enum class SndFileMajorFormat
    {
        ///<summary>Microsoft WAV format (little endian default)</summary>
        WAV = SF_FORMAT_WAV,
        ///<summary>Apple/SGI AIFF format (big endian)</summary>
        AIFF = SF_FORMAT_AIFF,
        ///<summary>Sun/NeXT AU format (big endian)</summary>
        AU = SF_FORMAT_AU,
        ///<summary>RAW PCM data</summary>
        RAW = SF_FORMAT_RAW,
        ///<summary>Ensoniq PARIS file format</summary>
        PAF = SF_FORMAT_PAF,
        ///<summary>Amiga IFF / SVX8 / SV16 format</summary>
        SVX = SF_FORMAT_SVX,
        ///<summary>Sphere NIST format</summary>
        NIST = SF_FORMAT_NIST,
        ///<summary>VOC files</summary>
        VOC = SF_FORMAT_VOC,
        ///<summary>Berkeley/IRCAM/CARL</summary>
        IRCAM = SF_FORMAT_IRCAM,
        ///<summary>Sonic Foundry's 64 bit RIFF/WAV</summary>
        W64 = SF_FORMAT_W64,
        ///<summary>Matlab (tm) V4.2 / GNU Octave 2.0</summary>
        MAT4 = SF_FORMAT_MAT4,
        ///<summary>Matlab (tm) V5.0 / GNU Octave 2.1</summary>
        MAT5 = SF_FORMAT_MAT5,
        ///<summary>Portable Voice Format</summary>
        PVF = SF_FORMAT_PVF,
        ///<summary>Fasttracker 2 Extended Instrument</summary>
        XI = SF_FORMAT_XI,
        ///<summary>HMM Tool Kit format</summary>
        HTK = SF_FORMAT_HTK,
        ///<summary>Midi Sample Dump Standard</summary>
        SDS = SF_FORMAT_SDS,
        ///<summary>Audio Visual Research</summary>
        AVR = SF_FORMAT_AVR,
        ///<summary>MS WAVE with WAVEFORMATEX</summary>
        WAVEX = SF_FORMAT_WAVEX,
        ///<summary>Sound Designer 2</summary>
        SD2 = SF_FORMAT_SD2,
        ///<summary>FLAC lossless file format</summary>
        FLAC = SF_FORMAT_FLAC,
        ///<summary>Core Audio File format</summary>
        CAF = SF_FORMAT_CAF,
        ///<summary>Psion WVE format</summary>
        WVE = SF_FORMAT_WVE,
        ///<summary>Xiph OGG container</summary>
        OGG = SF_FORMAT_OGG,
        ///<summary>Akai MPC 2000 sampler</summary>
        MPC2K = SF_FORMAT_MPC2K,
        ///<summary>RF64 WAV file</summary>
        RF64 = SF_FORMAT_RF64
    };


    [SerializableAttribute]
    [ComVisibleAttribute(true)]
    ///<summary>Format Sub-types (minor format types in SF_FORMAT_...) as defined by <a href="http://www.mega-nerd.com/libsndfile">libsndfile</a>.</summary>
    ///<remarks>A file type would consist of a major type (ie <see cref="SndFileMajorFormat"/>::WAV) bitwise
    /// ORed with a sub type (ie <see cref="SndFileSubFormat"/>::PCM_S8).
    ///</remarks>
    public enum class SndFileSubFormat
    {
        ///<summary>Signed 8 bit data</summary>
        PCM_S8 = SF_FORMAT_PCM_S8,
        ///<summary>Signed 16 bit data</summary>
        PCM_16 = SF_FORMAT_PCM_16,
        ///<summary>Signed 24 bit data</summary>
        PCM_24 = SF_FORMAT_PCM_24,
        ///<summary>Signed 32 bit data</summary>
        PCM_32 = SF_FORMAT_PCM_32,

        ///<summary>Unsigned 8 bit data (WAV and RAW only)</summary>
        PCM_U8 = SF_FORMAT_PCM_U8,

        ///<summary>32 bit float data</summary>
        FLOAT = SF_FORMAT_FLOAT,
        ///<summary>64 bit float data</summary>
        DOUBLE = SF_FORMAT_DOUBLE,

        ///<summary>U-Law encoded</summary>
        ULAW = SF_FORMAT_ULAW,
        ///<summary>A-Law encoded</summary>
        ALAW = SF_FORMAT_ALAW,
        ///<summary>IMA ADPCM</summary>
        IMA_ADPCM = SF_FORMAT_IMA_ADPCM,
        ///<summary>Microsoft ADPCM</summary>
        MS_ADPCM = SF_FORMAT_MS_ADPCM,

        ///<summary>GSM 6.10 encoding</summary>
        GSM610 = SF_FORMAT_GSM610,
        ///<summary>OKI / Dialogix ADPCM</summary>
        VOX_ADPCM = SF_FORMAT_VOX_ADPCM,

        ///<summary>32kbs G721 ADPCM encoding</summary>
        G721_32 = SF_FORMAT_G721_32,
        ///<summary>24kbs G723 ADPCM encoding</summary>
        G723_24 = SF_FORMAT_G723_24,
        ///<summary>40kbs G723 ADPCM encoding</summary>
        G723_40 = SF_FORMAT_G723_40,

        ///<summary>12 bit Delta Width Variable Word encoding</summary>
        DWVW_12 = SF_FORMAT_DWVW_12,
        ///<summary>16 bit Delta Width Variable Word encoding</summary>
        DWVW_16 = SF_FORMAT_DWVW_16,
        ///<summary>24 bit Delta Width Variable Word encoding</summary>
        DWVW_24 = SF_FORMAT_DWVW_24,
        ///<summary>N bit Delta Width Variable Word encoding</summary>
        DWVW_N = SF_FORMAT_DWVW_N,

        ///<summary>8 bit differential PCM (XI only)</summary>
        DPCM_8 = SF_FORMAT_DPCM_8,
        ///<summary>16 bit differential PCM (XI only)</summary>
        DPCM_16 = SF_FORMAT_DPCM_16,

        ///<summary>Xiph Vorbis encoding</summary>
        VORBIS = SF_FORMAT_VORBIS
    };


    [SerializableAttribute]
    [ComVisibleAttribute(true)]
    ///<summary>A SndFileInfo reference is passed at construction of any SndFile instance.</summary>
    ///<remarks>
    ///<para>For Writing access, the SndFileInfo instance shall be priorly filled in by user to describe the sound file characteristics.</para>
    ///<para>For Reading access, the SndFileInfo instance will reflect characteritics of the sound file.</para>
    ///</remarks>
    public ref struct SndFileInfo
    {
        ///<summary>Count of samples per second.</summary>
        int SamplesPerSecond;
        ///<summary>Count of audio channels.</summary>
        int Channels;
        ///<summary> Indicates the sound file format (SF_FORMAT_...) as defined by the <a href="http://www.mega-nerd.com/libsndfile">libsndfile</a> library.</summary>
        ///<remarks>
        ///Possible value is a bitwise ORed of one <see cref="SndFileMajorFormat"/> value and one compatible <see cref="SndFileSubFormat"/> value.
        ///</remarks>
        int Format;
    };

    [ComVisibleAttribute(true)]
    ///<summary>
    ///A managed class wrapper for the functions of the libsndfile library
    ///</summary>
    public ref class SndFile: public MarshalByRefObject
    {
    private:
        IO::FileStream^ const _InnerFileStream;

    protected:
        SNDFILE*        sndfileHandle;
        SndFileInfo^    sndFileInfo;

    public:
        ///<summary>
        ///Gets the inner FileStream instance used for access to the file.
        ///</summary>
        property IO::FileStream^ InnerFileStream
        { IO::FileStream^ get() { return _InnerFileStream; } }
        
        ///<summary>
        ///Initializes a new instance of the SndFile class.
        ///<para>According to the right access (Reading/Writing) of <paramref name="innerfileStream"/>,
        /// the sound file is opened for reading and/or writing.</para>
        ///</summary>
        ///<param name="innerfileStream">the FileStream instance used for access to the file.</param>
        ///<param name="info">the file info required for writing to a sound file or reflecting the sound file characteristics in reading access.</param>
        SndFile(
            [InAttribute] FileStream^ innerfileStream,
            [InAttribute] [OutAttribute] SndFileInfo^ info ):
        _InnerFileStream( innerfileStream )
        {
            SF_INFO sf_info;
            // native file handle
            IntPtr intPtr = innerfileStream->SafeFileHandle->DangerousGetHandle();
            int sf_mode;
            int _Flags;

            sndFileInfo = info;

            sf_info.samplerate = info->SamplesPerSecond;
            sf_info.channels = info->Channels;
            sf_info.format = info->Format;
            
            if( innerfileStream->CanRead )
            {
                if( innerfileStream->CanWrite )
                {
                    sf_mode = SFM_RDWR;
                    _Flags = _O_RDWR;
                }
                else
                {
                    sf_mode = SFM_READ;
                    _Flags = _O_RDONLY;
                }
            }
            else
            {
                sf_mode = SFM_WRITE;
                _Flags = _O_WRONLY;
            }

            // native file descriptor
            int fd = _open_osfhandle( (intptr_t) intPtr.ToPointer(), _Flags );
            if( fd < 0 )
                throw gcnew IOException( "_open_osfhandle() was not able to return a valid file descriptor");
            

            sndfileHandle = sf_open_fd( fd, sf_mode, &sf_info, FALSE );
            if( sndfileHandle == NULL )
            {                
                //TODO: return a sndfile error code
                throw gcnew IOException( "sf_opend_fd() has returned a null handle");
            }

            info->SamplesPerSecond = sf_info.samplerate;
            info->Channels = sf_info.channels;
            info->Format = sf_info.format;
        }


        ///<summary>
        ///Releases all resources used by the <see cref="SndFile"/>.
        ///</summary>
        !SndFile()
        {
            int sf_return;

            sf_write_sync( sndfileHandle );
            sf_return = sf_close( sndfileHandle );

            Diagnostics::Debug::Assert( sf_return == 0, "sf_close has returned a non null value:" + sf_return );
        }
        ///<summary>
        ///Releases all resources used by the <see cref="SndFile"/>.
        ///</summary>
        ~SndFile(){ this->!SndFile(); }


        ///<summary>
        ///Reads some PCM audio <paramref name="samples"/> from the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the sound file contains several channels items are interleaved in the output buffer.</para>
        ///<para>The count of items to be read must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///read the data using Read<b>&lt;Double&gt;</b>().
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        ///<param name="samples">Reference on the 1st element of the managed array where to store items read from the file.</param>
        ///<param name="countOfSamples">Count of items to read.</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if end of file has been reached.</returns>
        generic <typename T> where T: value class
            int Read( T% samples, int countOfSamples )
        {
            int countOfSamplesRead;
            pin_ptr<T> buffer = &samples;

            if      ( T::typeid == short::typeid )
                countOfSamplesRead = Read( (short*) buffer, countOfSamples );
            else if ( T::typeid == int::typeid )
                countOfSamplesRead = Read( (int*) buffer, countOfSamples );
            else if ( T::typeid == float::typeid )
                countOfSamplesRead = Read( (float*) buffer, countOfSamples );
            else if ( T::typeid == double::typeid )
                countOfSamplesRead = Read( (double*) buffer, countOfSamples );
            else
                throw gcnew InvalidDataException( T::typeid->Name + " is not a supported type for the parameter samples" );

            return countOfSamples;
        }

        ///<summary>
        ///Reads some PCM audio <paramref name="samples"/> from the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#read">libsndfile sf_read_short</a> function
        ///</summary>
        ///<remarks>
        ///<para>This function fills the native array pointed to by <paramref name="samples"/> with the requested <paramref name="countOfSamples"/>.</para>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be short.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and read the data using this function.
        /// libsndfile seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the native output array where to store items read from the file.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( short* samples, int countOfSamples)
        {
            return (int) sf_read_short( sndfileHandle, samples, countOfSamples);
       }

        ///<summary>
        ///Reads some PCM audio <paramref name="samples"/> from the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#read">libsndfile sf_read_int</a> function
        ///</summary>
        ///<remarks>
        ///<para>This function fills the native array pointed to by <paramref name="samples"/> with the requested <paramref name="countOfSamples"/>.</para>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be int.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and read the data using this function.
        /// libsndfile seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the native output array where to store items read from the file.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( int* samples, int countOfSamples)
        {
            return (int) sf_read_int( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Reads some PCM audio <paramref name="samples"/> from the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#read">libsndfile sf_read_float</a> function
        ///</summary>
        ///<remarks>
        ///<para>This function fills the native array pointed to by <paramref name="samples"/> with the requested <paramref name="countOfSamples"/>.</para>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be float.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and read the data using this function.
        /// libsndfile seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the native output array where to store items read from the file.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( float* samples, int countOfSamples)
        {
            return (int) sf_read_float( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Reads some PCM audio <paramref name="samples"/> from the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#read">libsndfile sf_read_double</a> function
        ///</summary>
        ///<remarks>
        ///<para>This function fills the native array pointed to by <paramref name="samples"/> with the requested <paramref name="countOfSamples"/>.</para>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be double.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and read the data using this function.
        /// libsndfile seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the native output array where to store items read from the file.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( double* samples, int countOfSamples)
        {
            return (int) sf_read_double( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Reads some <paramref name="samples"/> from the sound file. And store them in a managed Array.
        ///</summary>
        ///<remarks>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be of type <typeparamref name="T"/>.
        /// libsndfile seamlessly converts between the formats of the file and requested format <typeparamref name="T"/> on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the managed output array where to store items read from the file.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        generic <typename T> where T: value class int Read( array<T>^ samples, int countOfSamples)
        {
            return Read( samples[0], countOfSamples );
        }

        ///<summary>
        ///Reads some <paramref name="samples"/> from the sound file. And store them in a managed Array.
        ///</summary>
        ///<remarks>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be of type <typeparamref name="T"/>.
        /// libsndfile seamlessly converts between the formats of the file and requested format <typeparamref name="T"/> on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the managed output array where to store items read from the file.</param>
        ///<param name="index">Position in the output array where to start to store read samples.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        generic <typename T> where T: value class int Read( array<T>^ samples, int index, int countOfSamples)
        {
            return Read( samples[index], countOfSamples );
        }

        ///<summary>
        ///Reads some <paramref name="samples"/> from the sound file. And store them in a managed Array.
        ///</summary>
        ///<remarks>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be of type <paramref name="sampleType"/>.
        /// libsndfile seamlessly converts between the formats of the file and requested format <paramref name="sampleType"/> on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the managed output array where to store items read from the file.</param>
        ///<param name="sampleType">Type of the audio samples. This can be TypeCode::Int16, TypeCode::Int32, TypeCode::Single or TypeCode::Double</param>
        ///<param name="index">Position in the output array where to start to store read samples.</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( Array^ samples, Type^ sampleType, int index, int countOfSamples)
        {
            switch( Type::GetTypeCode(sampleType) )
            {
            case TypeCode::Int16:
                return Read( (array<short>^) samples, index, countOfSamples );
                break;
            case TypeCode::Int32:
                return Read( (array<int>^) samples, index, countOfSamples );
                break;
            case TypeCode::Single:
                return Read( (array<float>^) samples, index, countOfSamples );
                break;
            case TypeCode::Double:
                return Read( (array<double>^) samples, index, countOfSamples );
                break;
            default:
                throw gcnew ArgumentException( "type" + sampleType->Name + " is not valid for sampleType parameter" );
            }
        }

        ///<summary>
        ///Reads some <paramref name="samples"/> from the sound file. And store them in a managed Array.
        ///</summary>
        ///<remarks>
        ///<para>
        ///It is important to note that the data format of the file does not need necessary to be of type <paramref name="sampleType"/>.
        /// libsndfile seamlessly converts between the formats of the file and requested format <paramref name="sampleType"/> on-the-fly.
        ///</para>
        ///<para>
        ///If the sound file contains several audio channels, PCM samples of the different channels will be interleaved in the output array.
        ///</para>
        ///</remarks>
        ///<param name="samples">Pointer to the managed output array where to store items read from the file.</param>
        ///<param name="sampleType">Type of the audio samples. This can be TypeCode::Int16, TypeCode::Int32, TypeCode::Single or TypeCode::Double</param>
        ///<param name="countOfSamples">
        ///Count of PCM audio samples to read.
        ///It must be an integer product of the number of channels or an error will occur.
        ///</param>
        ///<returns>Count of items actually read. Can be less than <paramref name="countOfSamples"/> if the end of file has been reached.</returns>
        int Read( Array^ samples, Type^ sampleType, int countOfSamples)
        {
            return Read( samples, sampleType, 0, countOfSamples );
        }


        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///write samples of type Single.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        ///<param name="samples">Reference on the 1st element of the managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        generic <typename T> where T: value class int Write( T% samples, int countOfSamples )
        {
            int countOfSamplesWritten;
            pin_ptr<T> buffer = &samples;

            if( T::typeid == short::typeid )
                countOfSamplesWritten = Write( (short*) buffer, countOfSamples );
            else if(T::typeid == int::typeid)
                countOfSamplesWritten = Write( (int*) buffer, countOfSamples );
            else if(T::typeid == float::typeid)
                countOfSamplesWritten = Write( (float*) buffer, countOfSamples );
            else if(T::typeid == double::typeid)
                countOfSamplesWritten = Write( (double*) buffer, countOfSamples );
            else
                throw gcnew InvalidDataException( T::typeid->Name + " is not a supported type for the parameter samples" );

            return countOfSamplesWritten;
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#write">libsndfile sf_write_short</a> function
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and
        ///write samples using this method.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="samples">Reference on the 1st element of the managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( short* samples, int countOfSamples)
        {
            return (int) sf_write_short( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#write">libsndfile sf_write_int</a> function
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and
        ///write samples using this method.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="samples">Reference on the 1st element of the managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( int* samples, int countOfSamples)
        {
            return (int) sf_write_int( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#write">libsndfile sf_write_float</a> function
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and
        ///write samples using this method.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="samples">Reference on the 1st element of the managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( float* samples, int countOfSamples)
        {
            return (int) sf_write_float( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///Wrapper for the <a href="http://www.mega-nerd.com/libsndfile/api.html#write">libsndfile sf_write_double</a> function
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 8 bit PCM encoded WAV file and
        ///write samples using this method.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="samples">Reference on the 1st element of the managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( double* samples, int countOfSamples)
        {
            return (int) sf_write_double( sndfileHandle, samples, countOfSamples);
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///write samples of type Single.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        ///<param name="samples">Managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        generic <typename T> where T: value class int Write( array<T>^ samples, int countOfSamples)
        {
            return Write( samples[0], countOfSamples );
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///write samples of type Single.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<typeparam name="T">Type of the audio samples. This can be short, int, float or double</typeparam>
        ///<param name="samples">Managed array to be written to the file.</param>
        ///<param name="index">Index in the <paramref name="samples"/> where to start writing.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        generic <typename T> where T: value class int Write( array<T>^ samples, int index, int countOfSamples)
        {
            return Write( samples[index], countOfSamples );
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///write samples of type Single.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="sampleType">Type of the audio samples. This can be short, int, float or double</param>
        ///<param name="samples">Managed array to be written to the file.</param>
        ///<param name="index">Index in the <paramref name="samples"/> where to start writing.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( Array^ samples, Type^ sampleType, int index, int countOfSamples)
        {
            switch( Type::GetTypeCode(sampleType) )
            {
            case TypeCode::Int16:
                return Write( (array<short>^) samples, index, countOfSamples );
                break;
            case TypeCode::Int32:
                return Write( (array<int>^) samples, index, countOfSamples );
                break;
            case TypeCode::Single:
                return Write( (array<float>^) samples, index, countOfSamples );
                break;
            case TypeCode::Double:
                return Write( (array<double>^) samples, index, countOfSamples );
                break;
            default:
                throw gcnew ArgumentException( "type" + sampleType->Name + " is not valid for sampleType parameter" );
            }
        }

        ///<summary>
        ///Writes some PCM audio <paramref name="samples"/> to the sound file.
        ///</summary>
        ///<remarks>
        ///<para>If the SndFile instance has been initialized to contain several channels, items are interleaved in the target file.</para>
        ///<para>The count of items to be written must be an integer product of the number of channels.</para>
        ///<para>
        ///It is important to note that the data type used by the calling program and the data format of the file
        /// do not need to be the same.
        /// For instance, it is possible to open a 16 bit PCM encoded WAV file and
        ///write samples of type Single.
        /// The library seamlessly converts between the two formats on-the-fly.
        ///</para>
        ///</remarks>
        ///<param name="sampleType">Type of the audio samples. This can be short, int, float or double</param>
        ///<param name="samples">Managed array to be written to the file.</param>
        ///<param name="countOfSamples">Count of items to write.</param>
        ///<returns>Count of items written (which should be the same as the <paramref name="countOfSamples"/> parameter).</returns>
        int Write( Array^ samples, Type^ sampleType, int countOfSamples)
        {
            return Write( samples, sampleType, 0, countOfSamples );
        }
    };
}
